Introduction
This RMarkdown document is part of the Generic Skills Component (GSK) of the Course of the Foundation Studies Programme at Srishti Manipal Institute of Art, Design, and Technology, Bangalore India. The material is based on A Layered Grammar of Graphics by Hadley Wickham. The course is meant for First Year students pursuing a Degree in Art and Design.
The intent of this GSK part is to build Skill in coding in R, and also appreciate R as a way to metaphorically visualize information of various kinds, using predominantly geometric figures and structures.
All RMarkdown files combine code, text, web-images, and figures developed using code. Everything is text; code chunks are enclosed in fences (```)
Goals
- (Re)Understand different kinds of data variables
- Appreciate how they can be identified based on the Interrogative Pronouns they answer to
- Understand how each kind of variable lends itself to a specific choice of colour scale in the data visualization.
Pedagogical Note
The method followed will be based on PRIMM:
- PREDICT Inspect the code and guess at what the code might do, write predictions
- RUN the code provided and check what happens
- INFER what the
parameters of the code do and write comments to explain. What bells and whistles can you see?
- MODIFY the
parameters code provided to understand the options available. Write comments to show what you have aimed for and achieved.
- MAKE : take an idea/concept of your own, and graph it.
In the following, there is some boiler plate code demonstrating the use of colour palettes in R. There are places where YOUR TURN is mention; copy and play with the boiler plate code to see what happens !
Data
We will use the penguins dataset built into the palmerpenguins package. Your should try other datasets too!
Here is a glimpse of the data:
glimpse(penguins)
Rows: 344
Columns: 8
$ species <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel~
$ island <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse~
$ bill_length_mm <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, ~
$ bill_depth_mm <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, ~
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186~
$ body_mass_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, ~
$ sex <fct> male, female, female, NA, female, male, female, male~
$ year <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007~
Note that the unit of observation here is one-row-per-penguin.
Variables you need for this lab:


Colour vs fill aesthetic
Fill and colour scales in ggplot2 can use the same palettes. Some shapes such as lines only accept the colour aesthetic, while others, such as polygons, accept both colour and fill aesthetics. In the latter case, the colour refers to the border of the shape, and the fill to the interior.

All symbols have a foreground colour, so if we add color = "navy", they all are affected.
s + geom_point(aes(shape = z), size = 4, colour = "navy")

While all symbols have a foreground colour, symbols 21-25 also take a background colour (fill). So if we add fill = "orchid", only the last row of symbols are affected.
s + geom_point(aes(shape = z), size = 4, colour = "navy", fill = "orchid")

Discrete vs continuous variables
WHAT IS THE DIFFERENCE BETWEEN CATEGORICAL, ORDINAL AND INTERVAL VARIABLES?
In order to use color with your data, most importantly, you need to know if you’re dealing with discrete or continuous variables.
Some Colour Palette Packages in R
We have the following example packages that offer palettes in R:
RColorBrewer
wesanderson
paletteer
colorspace
See Appendix for a detailed graphical analysis of these palette packages.
Colour Palette Types
These palettes can be:
Sequential (type = “seq”) palettes are suited to ordered data that progress from low to high. Lightness steps dominate the look of these schemes, with light colors for low data values to dark colors for high data values. (for numerical data, that are ordered)
Diverging (type = “div”) palettes put equal emphasis on mid-range critical values and extremes at both ends of the data range. The critical class or break in the middle of the legend is emphasized with light colors and low and high extremes are emphasized with dark colors that have contrasting hues.(for numerical data that can be positive or negative, often representing deviations from some norm or baseline)
Qualitative (type = “qual”) palettes do not imply magnitude differences between legend classes, and hues are used to create the primary visual differences between classes. Qualitative schemes are best suited to representing nominal or categorical data. (for qualitative unordered data)
Create a simple set of scatter plots
We will create simple base plots in ggplot and see how we may alter the colour scales using palettes.
names(penguins)
[1] "species" "island" "bill_length_mm"
[4] "bill_depth_mm" "flipper_length_mm" "body_mass_g"
[7] "sex" "year"
p1 <- penguins %>%
drop_na() %>%
# pipe data into ggplot
# after removing data rows that have missing ( NA ) values
ggplot(aes(y = body_mass_g, x = flipper_length_mm,
color = species, # COLOUR=DISCRETE VARIABLE
shape = species)) +
geom_point() +
labs(title = "Default Colours in ggplot",
subtitle = "P1: DISCRETE Colour Palette")
p2 <-
penguins %>%
drop_na() %>%
# pipe the data into ggformula,
# after removing data rows that have missing ( NA ) values
ggplot(aes(y = body_mass_g, x = flipper_length_mm,
color = bill_length_mm, # COLOUR=DISCRETE VARIABLE
shape = species)) +
geom_point() +
labs(title = "Default Colours in ggplot",
subtitle = "P2: CONTINUOUS Colour Palette")
p1

p2

Note that these use the default colours in R.
Colours for Continuous Variables
The commands below are used to fill colours based on Quantitative Variables:
scale_colour/fill_gradient (Two colour gradient)
scale_colour/fill_gradient2 (Three colour gradient)
scale_colour/fill_gradientn (Specify Palette, from other packages also, like wesanderson )
scale_colour/fill_distiller (Palettes from RColorBrewer)
Colours for Discrete Variables
The commands below are used to fill colours based on Qualitative Variables:
scale_colour/fill_discrete
scale_colour/fill_brewer # RColorBrewer
Now to use these:
Plotting Colours based on Continuous Variables
Continuous Two Colour Gradients
Creates a pallete containing continuous shades between two colours:
p2 +
scale_color_gradient(
low = "yellow", # Play with this in the chunk below
high = "purple") + # Play with this in the chnk below
labs(title = "Two Colour Gradients",
subtitle = "P2: Continuous 2-Colour Pallete")

Continuous Three Colour Gradients
Sometimes we want a palette this way: a midpoint colour, and colours for the two extremes of a continuous variable:
colour_midpoint <- mean(penguins$bill_length_mm,
na.rm = TRUE) # remove missing values
# Struggled all morning on 22 Aug 2020 to get at this ;-D
# Play with the function: 0/mean/median/mode/max/min
p2 +
scale_colour_gradient2(
low = "brown", # Play with this in the chunk below
mid = "white", # Play with this in the chunk below
high = "purple", # Play with this in the chunk below
midpoint = colour_midpoint, # see above
space = "Lab", # don't mess with this!
na.value = "grey50") +
labs(title = "Three colour continuous gradient",
subtitle = "Mid Colour mapped to midpoint of data variable",
caption = "Colours inspired by my favourite cocker spaniel, Lord Chestnut") # Play with these

Continuous n-Colour Gradients - grDevices package
# grDevices Palettes
p2 +
scale_colour_gradientn(
colours = terrain.colors(10)) +
# Try these:
# heat.colors() / topo.colors() / cm.colors() / rainbow()
labs(title = "N-colour continuous gradients",
subtitle = "Palettes from grDevices",
caption = "Palette: terrain.colors")

Continuous n-Colour Gradients - wesanderson Palettes
names(wes_palettes)
[1] "BottleRocket1" "BottleRocket2" "Rushmore1" "Rushmore"
[5] "Royal1" "Royal2" "Zissou1" "Darjeeling1"
[9] "Darjeeling2" "Chevalier1" "FantasticFox1" "Moonrise1"
[13] "Moonrise2" "Moonrise3" "Cavalcanti1" "GrandBudapest1"
[17] "GrandBudapest2" "IsleofDogs1" "IsleofDogs2"
p2 +
scale_colour_gradientn(
colors = wes_palette(name = "GrandBudapest1",
n = 4), # Keep an eye on "n".
na.value = "grey") +
# Try these:
# "BottleRocket1" "BottleRocket2" "Rushmore1"
# "Rushmore" "Royal1" "Royal2"
# "Zissou1" "Darjeeling1" "Darjeeling2"
# "Chevalier1" "FantasticFox1" "Moonrise1"
# "Moonrise2" "Moonrise3" "Cavalcanti1"
# "GrandBudapest1" "GrandBudapest2" "IsleofDogs1"
# "IsleofDogs2"
# Keep an eye on "n".
labs(title = "N-colour continuous gradients",
subtitle = "Palettes from wesanderson",
caption = "Palette: GrandBudapest1") # Change this caption based on palette choice

Continuous n-Colour palettes from RColorBrewer
Recall Palette types
seq for continuous data mapped to colour
qual for categorical data mapped to colour ( discrete)
div continuous data mapped to colour, that has pos and neg extremes from a middle value
# scale_color_distiller() and scale_fill_distiller()
# are used to apply the ColorBrewer colour scales
# to continuous data.
p2 +
scale_colour_distiller(
palette = "YlGnBu") + # Play with this palette
labs(title = "RColorBrewer Palette")

Continuous Colour scales using paletteer palettes
This palette seems to have everything!
# What continuous palettes are there in paletteer?
paletteer::palettes_c_names
# A tibble: 330 x 3
package palette type
<chr> <chr> <chr>
1 gameofthrones targaryen sequential
2 gameofthrones targaryen2 sequential
3 gameofthrones stark sequential
4 gameofthrones stark2 sequential
5 gameofthrones lannister sequential
6 gameofthrones martell sequential
7 gameofthrones tully diverging
8 gameofthrones greyjoy sequential
9 gameofthrones baratheon sequential
10 gameofthrones baratheon2 sequential
# ... with 320 more rows
OK, one of the Games of Thrones Palettes!
p2 +
scale_colour_paletteer_c("gameofthrones::jon_snow") +
labs(
subtitle = "Continuous Palette - Game of Thrones",
caption = "Oh you awful Srishti people...") +
# Viridis Plasma Palette.
p2 +
scale_colour_paletteer_c("viridis::plasma") +
labs(subtitle = "Continuous Palette - Viridis:Plasma")

Plotting Colours based on Discrete Variables
Discrete n-Colour palettes from RColorBrewer
RColorBrewer::brewer.pal.info
maxcolors category colorblind
BrBG 11 div TRUE
PiYG 11 div TRUE
PRGn 11 div TRUE
PuOr 11 div TRUE
RdBu 11 div TRUE
RdGy 11 div FALSE
RdYlBu 11 div TRUE
RdYlGn 11 div FALSE
Spectral 11 div FALSE
Accent 8 qual FALSE
Dark2 8 qual TRUE
Paired 12 qual TRUE
Pastel1 9 qual FALSE
Pastel2 8 qual FALSE
Set1 9 qual FALSE
Set2 8 qual TRUE
Set3 12 qual FALSE
Blues 9 seq TRUE
BuGn 9 seq TRUE
BuPu 9 seq TRUE
GnBu 9 seq TRUE
Greens 9 seq TRUE
Greys 9 seq TRUE
Oranges 9 seq TRUE
OrRd 9 seq TRUE
PuBu 9 seq TRUE
PuBuGn 9 seq TRUE
PuRd 9 seq TRUE
Purples 9 seq TRUE
RdPu 9 seq TRUE
Reds 9 seq TRUE
YlGn 9 seq TRUE
YlGnBu 9 seq TRUE
YlOrBr 9 seq TRUE
YlOrRd 9 seq TRUE
RColorBrewer::display.brewer.all()

p1 +
# default palette = "Blues"
scale_colour_brewer() +
labs(title = "Brewer Palette = Blues")

p1 +
scale_color_brewer(palette = "Spectral") +
labs(title = "Brewer Palette = Spectral")

Discrete Colour scales using wesanderson palettes
wesanderson::wes_palettes %>% names()
[1] "BottleRocket1" "BottleRocket2" "Rushmore1" "Rushmore"
[5] "Royal1" "Royal2" "Zissou1" "Darjeeling1"
[9] "Darjeeling2" "Chevalier1" "FantasticFox1" "Moonrise1"
[13] "Moonrise2" "Moonrise3" "Cavalcanti1" "GrandBudapest1"
[17] "GrandBudapest2" "IsleofDogs1" "IsleofDogs2"
p1 +
scale_colour_discrete(type = wes_palette(name = "GrandBudapest1",
n = 3))

p1 +
scale_colour_manual(values = wesanderson::wes_palette(name = "Zissou1", n = 3))

Discrete n-Colour palettes from RColorBrewer
# scale_x_brewer() for DISCRETE data
p1 +
scale_colour_brewer(palette = "Spectral") +
labs(title = "RColorBrewer Palette")

Discrete Colour scales using paletteer palettes
palettes_d_names
# A tibble: 2,037 x 5
package palette length type novelty
<chr> <chr> <int> <chr> <lgl>
1 awtools a_palette 8 sequential TRUE
2 awtools ppalette 8 qualitative TRUE
3 awtools bpalette 16 qualitative TRUE
4 awtools gpalette 4 sequential TRUE
5 awtools mpalette 9 qualitative TRUE
6 awtools spalette 6 qualitative TRUE
7 basetheme brutal 10 qualitative TRUE
8 basetheme clean 10 qualitative TRUE
9 basetheme dark 10 qualitative TRUE
10 basetheme deepblue 10 qualitative TRUE
# ... with 2,027 more rows
palettes_dynamic_names
package palette length type
1 cartography blue.pal 20 sequential
2 cartography orange.pal 20 sequential
3 cartography red.pal 20 sequential
4 cartography brown.pal 20 sequential
5 cartography green.pal 20 sequential
6 cartography purple.pal 20 sequential
7 cartography pink.pal 20 sequential
8 cartography wine.pal 20 sequential
9 cartography grey.pal 20 sequential
10 cartography turquoise.pal 20 sequential
11 cartography sand.pal 20 sequential
12 cartography taupe.pal 20 sequential
13 cartography kaki.pal 20 sequential
14 cartography harmo.pal 20 sequential
15 cartography pastel.pal 20 qualitative
16 cartography multi.pal 20 qualitative
17 ggthemes_ptol qualitative 12 qualitative
18 ggthemes_solarized yellow 8 qualitative
19 ggthemes_solarized orange 8 qualitative
20 ggthemes_solarized red 8 qualitative
21 ggthemes_solarized magenta 8 qualitative
22 ggthemes_solarized violet 8 qualitative
23 ggthemes_solarized blue 8 qualitative
24 ggthemes_solarized cyan 8 qualitative
25 ggthemes_solarized green 8 qualitative
paletteer_d("dutchmasters::pearl_earring")
<colors>
#A65141FF #E7CDC2FF #80A0C7FF #394165FF #FCF9F0FF #B1934AFF #DCA258FF #100F14FF #8B9DAFFF #EEDA9DFF #E8DCCFFF
paletteer_dynamic("ggthemes_ptol::qualitative", n = 3)
<colors>
#4477AAFF #DDCC77FF #CC6677FF
p1 +
scale_colour_paletteer_d("ggthemes_ptol::qualitative",
dynamic = TRUE) +
labs(title = "Palettes from `paletteer`",
subtitle = "Dynamic Palette")

# I like Vermeer's "Girl with the Pearl Earring"!
p1 +
scale_colour_paletteer_d("dutchmasters::pearl_earring",
dynamic = FALSE) +
labs(title = "Palettes from `paletteer`",
subtitle = " Palette from Vermeer: Girl with Pearl Earring")

LS0tDQp0aXRsZTogIkxhYiAwNTogQ29sb3JzIHdpdGggUGVuZ3VpbnMiDQpzdWJ0aXRsZTogIlBhbGV0dGVzIGZyb20gRmFtb3VzIFBhaW50aW5ncywgR29ULCBhbmQgV2VzIEFuZGVyc29uIg0KYXV0aG9yOiAiQXJ2aW5kIFZlbmthdGFkcmkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIHRvY19kZXB0aDogMg0KICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUNCi0tLQ0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRSwgY2FjaGUgPSBGQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsIGNvbW1lbnQgPSBOQSwgd2FybmluZyA9IEZBTFNFLCBlcnJvcnMgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB0aWR5ID0gRkFMU0UsIGNhY2hlID0gRkFMU0UsIGZpZy5wYXRoPSAiMDMtZmlncy8iKQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBNYW5hZ2UgZGF0YQ0KbGlicmFyeShzY2FsZXMpICMgQ3JlYXRlIHNwZWNpYWwgKCAlIG9yICQgKSBzY2FsZXMNCiMNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpICMgc291cmNlIG9mIG91ciBkYXRhDQojDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgIyBDb2xvdXIgUGFsZXR0ZXMNCmxpYnJhcnkod2VzYW5kZXJzb24pICMgQ29sb3VyIFBhbGV0dGVzDQpsaWJyYXJ5KGdhbWVvZnRocm9uZXMpICMgWW91IGFsbCBrbm93IHRoaXMhDQojDQpsaWJyYXJ5KHBhbGV0dGVlcikgIyBDb2xvdXIgUGFsZXR0ZXMNCmxpYnJhcnkoY29sb3JzcGFjZSkgIyBDb2xvdXIgUGFsZXR0ZXMNCiMNCmxpYnJhcnkocGF0Y2h3b3JrKSAjIGFycmFuZ2VzIHBsb3RzIG9uIFJvdy1Db2wgDQpsaWJyYXJ5KGdndGhlbWVzKQ0KDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyBSTWFya2Rvd24gZG9jdW1lbnQgaXMgcGFydCBvZiB0aGUgR2VuZXJpYyBTa2lsbHMgQ29tcG9uZW50ICAoR1NLKSBvZiB0aGUgQ291cnNlIG9mIHRoZSAgRm91bmRhdGlvbiBTdHVkaWVzIFByb2dyYW1tZSBhdCBTcmlzaHRpIE1hbmlwYWwgSW5zdGl0dXRlIG9mIEFydCwgRGVzaWduLCBhbmQgVGVjaG5vbG9neSwgQmFuZ2Fsb3JlIEluZGlhLiBUaGUgbWF0ZXJpYWwgaXMgYmFzZWQgb24gKkEgTGF5ZXJlZCBHcmFtbWFyIG9mIEdyYXBoaWNzKiBieSBIYWRsZXkgV2lja2hhbS4gVGhlIGNvdXJzZSBpcyBtZWFudCBmb3IgRmlyc3QgWWVhciBzdHVkZW50cyBwdXJzdWluZyBhIERlZ3JlZSBpbiBBcnQgYW5kIERlc2lnbi4gDQoNClRoZSBpbnRlbnQgb2YgdGhpcyBHU0sgcGFydCBpcyB0byBidWlsZCBTa2lsbCBpbiBjb2RpbmcgaW4gUiwgYW5kIGFsc28gYXBwcmVjaWF0ZSBSIGFzIGEgd2F5IHRvIG1ldGFwaG9yaWNhbGx5IHZpc3VhbGl6ZSBpbmZvcm1hdGlvbiBvZiB2YXJpb3VzIGtpbmRzLCB1c2luZyBwcmVkb21pbmFudGx5IGdlb21ldHJpYyBmaWd1cmVzIGFuZCBzdHJ1Y3R1cmVzLg0KDQpBbGwgUk1hcmtkb3duIGZpbGVzIGNvbWJpbmUgY29kZSwgdGV4dCwgd2ViLWltYWdlcywgYW5kIGZpZ3VyZXMgZGV2ZWxvcGVkIHVzaW5nIGNvZGUuIEV2ZXJ5dGhpbmcgaXMgdGV4dDsgY29kZSBjaHVua3MgYXJlIGVuY2xvc2VkIGluICoqZmVuY2VzKiogKGBgYCkNCg0KIyBHb2Fscw0KDQotIChSZSlVbmRlcnN0YW5kIGRpZmZlcmVudCBraW5kcyBvZiBkYXRhIHZhcmlhYmxlcw0KLSBBcHByZWNpYXRlIGhvdyB0aGV5IGNhbiBiZSBpZGVudGlmaWVkIGJhc2VkIG9uIHRoZSAqSW50ZXJyb2dhdGl2ZSBQcm9ub3VucyogdGhleSBhbnN3ZXIgdG8NCi0gVW5kZXJzdGFuZCBob3cgZWFjaCBraW5kIG9mIHZhcmlhYmxlIGxlbmRzIGl0c2VsZiB0byBhIHNwZWNpZmljIGNob2ljZSBvZiAqKmNvbG91ciBzY2FsZSoqIGluIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24uDQoNCg0KIyBQZWRhZ29naWNhbCBOb3RlDQoNClRoZSBtZXRob2QgZm9sbG93ZWQgd2lsbCBiZSBiYXNlZCBvbg0KW1BSSU1NXShodHRwczovL2Jsb2dzLmtjbC5hYy51ay9jc2VyLzIwMTcvMDkvMDEvcHJpbW0tYS1zdHJ1Y3R1cmVkLWFwcHJvYWNoLXRvLXRlYWNoaW5nLXByb2dyYW1taW5nLyk6DQoNCi0gICAqKlBSRURJQ1QqKiBJbnNwZWN0IHRoZSBjb2RlIGFuZCBndWVzcyBhdCB3aGF0IHRoZSBjb2RlIG1pZ2h0IGRvLA0KICAgICoqd3JpdGUgcHJlZGljdGlvbnMqKg0KLSAgICoqUlVOKiogdGhlIGNvZGUgcHJvdmlkZWQgYW5kIGNoZWNrIHdoYXQgaGFwcGVucw0KLSAgICoqSU5GRVIqKiB3aGF0IHRoZSBgcGFyYW1ldGVyc2Agb2YgdGhlIGNvZGUgZG8gYW5kICoqd3JpdGUgY29tbWVudHMgdG8gZXhwbGFpbioqLiBXaGF0IGJlbGxzIGFuZCB3aGlzdGxlcyBjYW4geW91IHNlZT8NCi0gICAqKk1PRElGWSoqIHRoZSBgcGFyYW1ldGVyc2AgY29kZSBwcm92aWRlZCB0byB1bmRlcnN0YW5kIHRoZQ0KICAgIGBvcHRpb25zYCBhdmFpbGFibGUuICoqV3JpdGUgY29tbWVudHMqKiB0byBzaG93IHdoYXQgeW91IGhhdmUgYWltZWQgZm9yIGFuZCBhY2hpZXZlZC4NCi0gICAqKk1BS0UqKiA6IHRha2UgYW4gaWRlYS9jb25jZXB0IG9mIHlvdXIgb3duLCBhbmQgZ3JhcGggaXQuDQoNCkluIHRoZSBmb2xsb3dpbmcsIHRoZXJlIGlzIHNvbWUgYm9pbGVyIHBsYXRlIGNvZGUgZGVtb25zdHJhdGluZyB0aGUgdXNlIG9mIGNvbG91ciBwYWxldHRlcyBpbiBSLiBUaGVyZSBhcmUgcGxhY2VzIHdoZXJlICoqWU9VUiBUVVJOKiogaXMgbWVudGlvbjsgY29weSBhbmQgcGxheSB3aXRoIHRoZSBib2lsZXIgcGxhdGUgY29kZSB0byBzZWUgd2hhdCBoYXBwZW5zICENCg0KIyBEYXRhDQoNCldlIHdpbGwgdXNlIHRoZSBgcGVuZ3VpbnNgIGRhdGFzZXQgYnVpbHQgaW50byB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlLiBZb3VyIHNob3VsZCB0cnkgb3RoZXIgZGF0YXNldHMgdG9vIQ0KDQpIZXJlIGlzIGEgZ2xpbXBzZSBvZiB0aGUgZGF0YToNCg0KYGBge3J9DQpnbGltcHNlKHBlbmd1aW5zKQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGUgdW5pdCBvZiBvYnNlcnZhdGlvbiBoZXJlIGlzIG9uZS1yb3ctcGVyLXBlbmd1aW4uDQoNClZhcmlhYmxlcyB5b3UgbmVlZCBmb3IgdGhpcyBsYWI6DQoNCiFbXShodHRwczovL2FsbGlzb25ob3JzdC5naXRodWIuaW8vcGFsbWVycGVuZ3VpbnMvcmVmZXJlbmNlL2ZpZ3VyZXMvbHRlcl9wZW5ndWlucy5wbmcpDQoNCiFbXShodHRwczovL2FsbGlzb25ob3JzdC5naXRodWIuaW8vcGFsbWVycGVuZ3VpbnMvcmVmZXJlbmNlL2ZpZ3VyZXMvY3VsbWVuX2RlcHRoLnBuZykNCg0KDQojIENvbG91ciB2cyBmaWxsIGFlc3RoZXRpYw0KDQpGaWxsIGFuZCBjb2xvdXIgc2NhbGVzIGluIGdncGxvdDIgY2FuIHVzZSB0aGUgc2FtZSBwYWxldHRlcy4gU29tZSBzaGFwZXMgc3VjaCBhcyBsaW5lcyBvbmx5IGFjY2VwdCB0aGUgY29sb3VyIGFlc3RoZXRpYywgd2hpbGUgb3RoZXJzLCBzdWNoIGFzIHBvbHlnb25zLCBhY2NlcHQgYm90aCBjb2xvdXIgYW5kIGZpbGwgYWVzdGhldGljcy4gSW4gdGhlIGxhdHRlciBjYXNlLCB0aGUgY29sb3VyIHJlZmVycyB0byB0aGUgYm9yZGVyIG9mIHRoZSBzaGFwZSwgYW5kIHRoZSBmaWxsIHRvIHRoZSBpbnRlcmlvci4NCg0KDQoNCmBgYHtyIGVjaG8gPSBGQUxTRX0NCiMjIEEgbG9vayBhdCBhbGwgMjUgc3ltYm9scw0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gMTo1LCANCiAgICAgICAgICAgICAgICAgIHkgPSByZXAocmV2KHNlcSgwLCAyNCwgYnkgPSA1KSksIGVhY2ggPSA1KSwgDQogICAgICAgICAgICAgICAgICB6ID0gMToyNSkNCnMgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IHgsIHkgPSB5KSkgKyANCiAgc2NhbGVfc2hhcGVfaWRlbnRpdHkoKSArIA0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0geiwgeSA9IHkgLSAxKSkgKyANCiAgdGhlbWVfdm9pZCgpDQpzICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSB6KSwgc2l6ZSA9IDQpIA0KYGBgDQoNCg0KQWxsIHN5bWJvbHMgaGF2ZSBhIGZvcmVncm91bmQgY29sb3VyLCBzbyBpZiB3ZSBhZGQgYGNvbG9yID0gIm5hdnkiYCwgdGhleSBhbGwgYXJlIGFmZmVjdGVkLg0KDQpgYGB7cn0NCnMgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHopLCBzaXplID0gNCwgY29sb3VyID0gIm5hdnkiKSANCmBgYA0KDQoNCldoaWxlIGFsbCBzeW1ib2xzIGhhdmUgYSBmb3JlZ3JvdW5kIGNvbG91ciwgc3ltYm9scyAyMS0yNSBhbHNvIHRha2UgYSBiYWNrZ3JvdW5kIGNvbG91ciAoZmlsbCkuIFNvIGlmIHdlIGFkZCBgZmlsbCA9ICJvcmNoaWQiYCwgb25seSB0aGUgbGFzdCByb3cgb2Ygc3ltYm9scyBhcmUgYWZmZWN0ZWQuDQoNCmBgYHtyfQ0KcyArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0geiksIHNpemUgPSA0LCBjb2xvdXIgPSAibmF2eSIsIGZpbGwgPSAib3JjaGlkIikgDQpgYGANCg0KDQojIERpc2NyZXRlIHZzIGNvbnRpbnVvdXMgdmFyaWFibGVzDQoNCltXSEFUIElTIFRIRSBESUZGRVJFTkNFIEJFVFdFRU4gQ0FURUdPUklDQUwsIE9SRElOQUwgQU5EIElOVEVSVkFMIFZBUklBQkxFUz9dKGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9vdGhlci9tdWx0LXBrZy93aGF0c3RhdC93aGF0LWlzLXRoZS1kaWZmZXJlbmNlLWJldHdlZW4tY2F0ZWdvcmljYWwtb3JkaW5hbC1hbmQtaW50ZXJ2YWwtdmFyaWFibGVzLykNCg0KSW4gb3JkZXIgdG8gdXNlIGNvbG9yIHdpdGggeW91ciBkYXRhLCBtb3N0IGltcG9ydGFudGx5LCB5b3UgbmVlZCB0byBrbm93IGlmIHlvdeKAmXJlIGRlYWxpbmcgd2l0aCBkaXNjcmV0ZSBvciBjb250aW51b3VzIHZhcmlhYmxlcy4gDQoNCg0KIyMgU29tZSBDb2xvdXIgUGFsZXR0ZSBQYWNrYWdlcyBpbiBSDQoNCldlIGhhdmUgdGhlIGZvbGxvd2luZyBleGFtcGxlIHBhY2thZ2VzIHRoYXQgb2ZmZXIgcGFsZXR0ZXMgaW4gUjoNCg0KLSBgUkNvbG9yQnJld2VyYA0KLSBgd2VzYW5kZXJzb25gDQotIGBwYWxldHRlZXJgDQotIGBjb2xvcnNwYWNlYA0KDQpTZWUgQXBwZW5kaXggZm9yIGEgZGV0YWlsZWQgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIHRoZXNlIHBhbGV0dGUgcGFja2FnZXMuIA0KDQoNCiMjIENvbG91ciBQYWxldHRlIFR5cGVzDQoNClRoZXNlIHBhbGV0dGVzIGNhbiBiZToNCg0KPiAqKlNlcXVlbnRpYWwqKiAodHlwZSA9ICJzZXEiKSBwYWxldHRlcyBhcmUgc3VpdGVkIHRvIG9yZGVyZWQgZGF0YSB0aGF0IHByb2dyZXNzIGZyb20gbG93IHRvIGhpZ2guIExpZ2h0bmVzcyBzdGVwcyBkb21pbmF0ZSB0aGUgbG9vayBvZiB0aGVzZSBzY2hlbWVzLCB3aXRoIGxpZ2h0IGNvbG9ycyBmb3IgbG93IGRhdGEgdmFsdWVzIHRvIGRhcmsgY29sb3JzIGZvciBoaWdoIGRhdGEgdmFsdWVzLiAoZm9yICoqbnVtZXJpY2FsKiogZGF0YSwgdGhhdCBhcmUgb3JkZXJlZCkNCg0KPiAqKkRpdmVyZ2luZyoqICh0eXBlID0gImRpdiIpIHBhbGV0dGVzIHB1dCBlcXVhbCBlbXBoYXNpcyBvbiBtaWQtcmFuZ2UgY3JpdGljYWwgdmFsdWVzIGFuZCBleHRyZW1lcyBhdCBib3RoIGVuZHMgb2YgdGhlIGRhdGEgcmFuZ2UuIFRoZSBjcml0aWNhbCBjbGFzcyBvciBicmVhayBpbiB0aGUgbWlkZGxlIG9mIHRoZSBsZWdlbmQgaXMgZW1waGFzaXplZCB3aXRoIGxpZ2h0IGNvbG9ycyBhbmQgbG93IGFuZCBoaWdoIGV4dHJlbWVzIGFyZSBlbXBoYXNpemVkIHdpdGggZGFyayBjb2xvcnMgdGhhdCBoYXZlIGNvbnRyYXN0aW5nIGh1ZXMuKGZvciAqKm51bWVyaWNhbCBkYXRhIHRoYXQgY2FuIGJlIHBvc2l0aXZlIG9yIG5lZ2F0aXZlKiosIG9mdGVuIHJlcHJlc2VudGluZyBkZXZpYXRpb25zIGZyb20gc29tZSBub3JtIG9yIGJhc2VsaW5lKQ0KDQo+ICoqUXVhbGl0YXRpdmUqKiAodHlwZSA9ICJxdWFsIikgcGFsZXR0ZXMgZG8gbm90IGltcGx5IG1hZ25pdHVkZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGxlZ2VuZCBjbGFzc2VzLCBhbmQgaHVlcyBhcmUgdXNlZCB0byBjcmVhdGUgdGhlIHByaW1hcnkgdmlzdWFsIGRpZmZlcmVuY2VzIGJldHdlZW4gY2xhc3Nlcy4gUXVhbGl0YXRpdmUgc2NoZW1lcyBhcmUgYmVzdCBzdWl0ZWQgdG8gcmVwcmVzZW50aW5nICoqbm9taW5hbCBvciBjYXRlZ29yaWNhbCoqIGRhdGEuIChmb3IgcXVhbGl0YXRpdmUgdW5vcmRlcmVkIGRhdGEpDQoNCg0KIyMgQ3JlYXRlIGEgc2ltcGxlIHNldCBvZiBzY2F0dGVyIHBsb3RzDQoNCldlIHdpbGwgY3JlYXRlIHNpbXBsZSBiYXNlIHBsb3RzIGluIGBnZ3Bsb3RgIGFuZCBzZWUgaG93IHdlIG1heSBhbHRlciB0aGUgY29sb3VyIHNjYWxlcyB1c2luZyBwYWxldHRlcy4NCg0KDQpgYGB7ciBwZW5ndWluLW5hbWVzIH0NCg0KbmFtZXMocGVuZ3VpbnMpDQoNCmBgYA0KDQoNCmBgYHtyIFBlbmdpdW5zIGRlZmF1bHQgc2ltcGxlIHBsb3R9DQpwMSA8LSBwZW5ndWlucyAlPiUgDQogIGRyb3BfbmEoKSAlPiUgDQogICMgcGlwZSBkYXRhIGludG8gZ2dwbG90DQogICMgYWZ0ZXIgcmVtb3ZpbmcgZGF0YSByb3dzIHRoYXQgaGF2ZSBtaXNzaW5nICggTkEgKSB2YWx1ZXMNCiAgZ2dwbG90KGFlcyh5ID0gYm9keV9tYXNzX2csIHggPSBmbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgIGNvbG9yID0gc3BlY2llcywgIyBDT0xPVVI9RElTQ1JFVEUgVkFSSUFCTEUNCiAgICAgICAgICAgc2hhcGUgPSBzcGVjaWVzKSkgKw0KICAgICAgICAgICBnZW9tX3BvaW50KCkgKyANCiAgICAgICAgICAgbGFicyh0aXRsZSA9ICJEZWZhdWx0IENvbG91cnMgaW4gZ2dwbG90IiwgDQogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiUDE6IERJU0NSRVRFIENvbG91ciBQYWxldHRlIikNCg0KDQpwMiA8LSANCnBlbmd1aW5zICU+JSANCiAgZHJvcF9uYSgpICU+JSANCiAgIyBwaXBlIHRoZSBkYXRhIGludG8gZ2dmb3JtdWxhLCANCiAgIyBhZnRlciByZW1vdmluZyBkYXRhIHJvd3MgdGhhdCBoYXZlIG1pc3NpbmcgKCBOQSApIHZhbHVlcw0KICBnZ3Bsb3QoYWVzKHkgPSBib2R5X21hc3NfZywgeCA9IGZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgY29sb3IgPSBiaWxsX2xlbmd0aF9tbSwgIyBDT0xPVVI9RElTQ1JFVEUgVkFSSUFCTEUNCiAgICAgICAgICAgc2hhcGUgPSBzcGVjaWVzKSkgKw0KICAgICAgICAgICBnZW9tX3BvaW50KCkgKyANCiAgICAgICAgICAgbGFicyh0aXRsZSA9ICJEZWZhdWx0IENvbG91cnMgaW4gZ2dwbG90IiwgDQogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiUDI6IENPTlRJTlVPVVMgQ29sb3VyIFBhbGV0dGUiKQ0KDQpwMQ0KcDINCmBgYA0KDQpOb3RlIHRoYXQgdGhlc2UgdXNlIHRoZSAqKmRlZmF1bHQgY29sb3VycyoqIGluIFIuIA0KDQoNCiMgQ29sb3VycyBmb3IgKipDb250aW51b3VzKiogVmFyaWFibGVzDQoNClRoZSBjb21tYW5kcyBiZWxvdyBhcmUgdXNlZCB0byBmaWxsIGNvbG91cnMgYmFzZWQgb24gUXVhbnRpdGF0aXZlIFZhcmlhYmxlczoNCg0KMSkgYHNjYWxlX2NvbG91ci9maWxsX2dyYWRpZW50YCAoVHdvIGNvbG91ciBncmFkaWVudCkNCjIpIGBzY2FsZV9jb2xvdXIvZmlsbF9ncmFkaWVudDJgIChUaHJlZSBjb2xvdXIgZ3JhZGllbnQpDQozKSBgc2NhbGVfY29sb3VyL2ZpbGxfZ3JhZGllbnRuYCAoU3BlY2lmeSBQYWxldHRlLCBmcm9tIG90aGVyIHBhY2thZ2VzIGFsc28sIGxpa2UgYHdlc2FuZGVyc29uYCApDQo0KSBgc2NhbGVfY29sb3VyL2ZpbGxfZGlzdGlsbGVyYCAoUGFsZXR0ZXMgZnJvbSBSQ29sb3JCcmV3ZXIpDQoNCg0KIyBDb2xvdXJzIGZvciAqKkRpc2NyZXRlKiogVmFyaWFibGVzDQoNClRoZSBjb21tYW5kcyBiZWxvdyBhcmUgdXNlZCB0byBmaWxsIGNvbG91cnMgYmFzZWQgb24gUXVhbGl0YXRpdmUgVmFyaWFibGVzOg0KDQoxKSBgc2NhbGVfY29sb3VyL2ZpbGxfZGlzY3JldGVgDQoyKSBgc2NhbGVfY29sb3VyL2ZpbGxfYnJld2VyYCAjIFJDb2xvckJyZXdlcg0KMykNCjQpIA0KDQpOb3cgdG8gdXNlIHRoZXNlOg0KDQojIFBsb3R0aW5nIENvbG91cnMgYmFzZWQgb24gQ29udGludW91cyBWYXJpYWJsZXMNCg0KIyMgQ29udGludW91cyBUd28gQ29sb3VyIEdyYWRpZW50cw0KDQpDcmVhdGVzIGEgcGFsbGV0ZSBjb250YWluaW5nICpjb250aW51b3VzKiBzaGFkZXMgYmV0d2VlbiB0d28gY29sb3VyczoNCg0KYGBge3IgQ29udGludW91cyBUd28gQ29sb3VyIEdyYWRpZW50c30NCnAyICsNCiAgICBzY2FsZV9jb2xvcl9ncmFkaWVudCgNCiAgICAgIGxvdyA9ICJ5ZWxsb3ciLCAjIFBsYXkgd2l0aCB0aGlzIGluIHRoZSBjaHVuayBiZWxvdw0KICAgICAgaGlnaCA9ICJwdXJwbGUiKSArICMgUGxheSB3aXRoIHRoaXMgaW4gdGhlIGNobmsgYmVsb3cNCiAgDQogIGxhYnModGl0bGUgPSAiVHdvIENvbG91ciBHcmFkaWVudHMiLA0KICAgICAgICAgIHN1YnRpdGxlID0gIlAyOiBDb250aW51b3VzIDItQ29sb3VyIFBhbGxldGUiKQ0KDQpgYGANCg0KYGBge3IgWU9VUiBUVVJOLTF9DQoNCmBgYA0KDQoNCiMjIENvbnRpbnVvdXMgVGhyZWUgQ29sb3VyIEdyYWRpZW50cw0KDQpTb21ldGltZXMgd2Ugd2FudCBhIHBhbGV0dGUgdGhpcyB3YXk6IGEgKm1pZHBvaW50KiBjb2xvdXIsIGFuZCBjb2xvdXJzIGZvciB0aGUgdHdvIGV4dHJlbWVzIG9mIGEgY29udGludW91cyB2YXJpYWJsZToNCg0KYGBge3IgQ29udGludW91cyBUaHJlZSBDb2xvdXIgR3JhZGllbnRzfQ0KDQpjb2xvdXJfbWlkcG9pbnQgPC0gbWVhbihwZW5ndWlucyRiaWxsX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSAjIHJlbW92ZSBtaXNzaW5nIHZhbHVlcw0KIyBTdHJ1Z2dsZWQgYWxsIG1vcm5pbmcgb24gMjIgQXVnIDIwMjAgdG8gZ2V0IGF0IHRoaXMgOy1EDQoNCiMgUGxheSB3aXRoIHRoZSBmdW5jdGlvbjogMC9tZWFuL21lZGlhbi9tb2RlL21heC9taW4NCg0KcDIgKw0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKA0KICBsb3cgPSAiYnJvd24iLCAjIFBsYXkgd2l0aCB0aGlzIGluIHRoZSBjaHVuayBiZWxvdw0KICBtaWQgPSAid2hpdGUiLCAjIFBsYXkgd2l0aCB0aGlzIGluIHRoZSBjaHVuayBiZWxvdw0KICBoaWdoID0gInB1cnBsZSIsICMgUGxheSB3aXRoIHRoaXMgaW4gdGhlIGNodW5rIGJlbG93DQogIG1pZHBvaW50ID0gY29sb3VyX21pZHBvaW50LCAjIHNlZSBhYm92ZQ0KICBzcGFjZSA9ICJMYWIiLCAjIGRvbid0IG1lc3Mgd2l0aCB0aGlzIQ0KICBuYS52YWx1ZSA9ICJncmV5NTAiKSAgKw0KICBsYWJzKHRpdGxlID0gIlRocmVlIGNvbG91ciBjb250aW51b3VzIGdyYWRpZW50IiwgDQogICAgICAgICAgc3VidGl0bGUgPSAiTWlkIENvbG91ciBtYXBwZWQgdG8gbWlkcG9pbnQgb2YgZGF0YSB2YXJpYWJsZSIsDQogICAgICAgICAgY2FwdGlvbiA9ICJDb2xvdXJzIGluc3BpcmVkIGJ5IG15IGZhdm91cml0ZSBjb2NrZXIgc3BhbmllbCwgTG9yZCBDaGVzdG51dCIpICMgUGxheSB3aXRoIHRoZXNlDQoNCmBgYA0KDQpgYGB7ciBZT1VSIFRVUk4tMn0NCg0KYGBgDQoNCiMjIENvbnRpbnVvdXMgbi1Db2xvdXIgR3JhZGllbnRzIC0gZ3JEZXZpY2VzIHBhY2thZ2UNCg0KDQpgYGB7ciBDb250aW51b3VzIG4tQ29sb3VyIEdyYWRpZW50cyAtIGdyRGV2aWNlc30NCiMgZ3JEZXZpY2VzIFBhbGV0dGVzDQpwMiArDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oDQogICAgY29sb3VycyA9IHRlcnJhaW4uY29sb3JzKDEwKSkgKw0KICAjIFRyeSB0aGVzZToNCiAgIyBoZWF0LmNvbG9ycygpIC8gdG9wby5jb2xvcnMoKSAvIGNtLmNvbG9ycygpIC8gcmFpbmJvdygpDQogIA0KICBsYWJzKHRpdGxlID0gIk4tY29sb3VyIGNvbnRpbnVvdXMgZ3JhZGllbnRzIiwgDQogICAgICAgICAgc3VidGl0bGUgPSAiUGFsZXR0ZXMgZnJvbSBnckRldmljZXMiLA0KICAgICAgICAgIGNhcHRpb24gPSAiUGFsZXR0ZTogdGVycmFpbi5jb2xvcnMiKQ0KYGBgDQoNCg0KYGBge3IgWU9VUiBUVVJOLTN9DQoNCmBgYA0KDQoNCiMjIENvbnRpbnVvdXMgbi1Db2xvdXIgR3JhZGllbnRzIC0gYHdlc2FuZGVyc29uYCBQYWxldHRlcw0KDQpgYGB7ciB3ZXNhbmRlcnNvbn0NCm5hbWVzKHdlc19wYWxldHRlcykNCg0KYGBgDQoNCmBgYHtyIFVzaW5nLXdlc2FuZGVyc29ufQ0KcDIgKw0KICAgIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oDQogICAgICBjb2xvcnMgPSB3ZXNfcGFsZXR0ZShuYW1lID0gIkdyYW5kQnVkYXBlc3QxIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gNCksICMgS2VlcCBhbiBleWUgb24gIm4iLg0KICAgICAgbmEudmFsdWUgPSAiZ3JleSIpICsNCiAgIyBUcnkgdGhlc2U6DQogICMgIkJvdHRsZVJvY2tldDEiICAiQm90dGxlUm9ja2V0MiIgICJSdXNobW9yZTEiDQogICMgIlJ1c2htb3JlIiAgICAgICAiUm95YWwxIiAgICAgICAgICJSb3lhbDIiDQogICMgIlppc3NvdTEiICAgICAgICAiRGFyamVlbGluZzEiICAgICJEYXJqZWVsaW5nMiIgICANCiAgIyAiQ2hldmFsaWVyMSIgICAgICJGYW50YXN0aWNGb3gxIiAgIk1vb25yaXNlMSIgICAgIA0KICAjICJNb29ucmlzZTIiICAgICAgIk1vb25yaXNlMyIgICAgICAiQ2F2YWxjYW50aTEiICAgDQogICMgIkdyYW5kQnVkYXBlc3QxIiAiR3JhbmRCdWRhcGVzdDIiICJJc2xlb2ZEb2dzMSIgICANCiAgIyAiSXNsZW9mRG9nczIiICAgDQogICMgS2VlcCBhbiBleWUgb24gIm4iLg0KICANCiAgbGFicyh0aXRsZSA9ICJOLWNvbG91ciBjb250aW51b3VzIGdyYWRpZW50cyIsIA0KICAgICAgIHN1YnRpdGxlID0gIlBhbGV0dGVzIGZyb20gd2VzYW5kZXJzb24iLA0KICAgICAgIGNhcHRpb24gPSAiUGFsZXR0ZTogR3JhbmRCdWRhcGVzdDEiKSAjIENoYW5nZSB0aGlzIGNhcHRpb24gYmFzZWQgb24gcGFsZXR0ZSBjaG9pY2UNCg0KYGBgDQoNCmBgYHtyIFlPVVItVFVSTi00fQ0KDQpgYGANCg0KDQojIyBDb250aW51b3VzIG4tQ29sb3VyIHBhbGV0dGVzIGZyb20gYFJDb2xvckJyZXdlcmANCg0KUmVjYWxsIFBhbGV0dGUgKip0eXBlcyoqDQoNCi0gYHNlcWAgZm9yIGNvbnRpbnVvdXMgZGF0YSBtYXBwZWQgdG8gY29sb3VyDQotIGBxdWFsYCBmb3IgY2F0ZWdvcmljYWwgZGF0YSBtYXBwZWQgdG8gY29sb3VyICggZGlzY3JldGUpDQotIGBkaXZgIGNvbnRpbnVvdXMgZGF0YSBtYXBwZWQgdG8gY29sb3VyLCB0aGF0IGhhcyBwb3MgYW5kIG5lZyBleHRyZW1lcyBmcm9tIGEgbWlkZGxlIHZhbHVlDQoNCg0KYGBge3IgVXNpbmctQ29sb3ItQnJld2VyLXBhbGV0dGVzfQ0KDQojIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcigpIGFuZCBzY2FsZV9maWxsX2Rpc3RpbGxlcigpIA0KIyBhcmUgdXNlZCB0byBhcHBseSB0aGUgQ29sb3JCcmV3ZXIgY29sb3VyIHNjYWxlcyANCiMgdG8gY29udGludW91cyBkYXRhLg0KDQpwMiArDQogIHNjYWxlX2NvbG91cl9kaXN0aWxsZXIoDQogICAgcGFsZXR0ZSA9ICJZbEduQnUiKSArICMgUGxheSB3aXRoIHRoaXMgcGFsZXR0ZQ0KICANCiAgbGFicyh0aXRsZSA9ICJSQ29sb3JCcmV3ZXIgUGFsZXR0ZSIpDQoNCmBgYA0KDQpgYGB7ciBZT1VSIFRVUk4tNX0NCg0KYGBgDQoNCiMjIENvbnRpbnVvdXMgQ29sb3VyIHNjYWxlcyB1c2luZyBgcGFsZXR0ZWVyYCBwYWxldHRlcw0KDQpUaGlzIHBhbGV0dGUgc2VlbXMgdG8gaGF2ZSBldmVyeXRoaW5nIQ0KDQpgYGB7ciBwYWxldHRlZXItcGFsZXR0ZXMtbGlzdH0NCiMgV2hhdCBjb250aW51b3VzIHBhbGV0dGVzIGFyZSB0aGVyZSBpbiBwYWxldHRlZXI/DQpwYWxldHRlZXI6OnBhbGV0dGVzX2NfbmFtZXMNCmBgYA0KDQoNCk9LLCBvbmUgb2YgdGhlIEdhbWVzIG9mIFRocm9uZXMgUGFsZXR0ZXMhIA0KDQpgYGB7ciBwYWxldHRlZXItY29udGludW91c30NCnAyICsNCiAgc2NhbGVfY29sb3VyX3BhbGV0dGVlcl9jKCJnYW1lb2Z0aHJvbmVzOjpqb25fc25vdyIpICsNCiAgDQogICAgbGFicygNCiAgICBzdWJ0aXRsZSA9ICJDb250aW51b3VzIFBhbGV0dGUgLSBHYW1lIG9mIFRocm9uZXMiLA0KICAgIGNhcHRpb24gPSAiT2ggeW91IGF3ZnVsIFNyaXNodGkgcGVvcGxlLi4uIikgKw0KICANCiMgVmlyaWRpcyBQbGFzbWEgUGFsZXR0ZS4gDQpwMiArDQogIHNjYWxlX2NvbG91cl9wYWxldHRlZXJfYygidmlyaWRpczo6cGxhc21hIikgKw0KICANCiAgbGFicyhzdWJ0aXRsZSA9ICJDb250aW51b3VzIFBhbGV0dGUgLSBWaXJpZGlzOlBsYXNtYSIpDQoNCmBgYA0KDQpgYGB7ciBZT1VSIFRVUk4tNn0NCg0KYGBgDQogDQoNCiMgUGxvdHRpbmcgQ29sb3VycyBiYXNlZCBvbiBEaXNjcmV0ZSBWYXJpYWJsZXMNCg0KIyMgRGlzY3JldGUgbi1Db2xvdXIgcGFsZXR0ZXMgZnJvbSBgUkNvbG9yQnJld2VyYA0KDQpgYGB7ciBicmV3ZXItcGFsZXR0ZXN9DQpSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwuaW5mbw0KUkNvbG9yQnJld2VyOjpkaXNwbGF5LmJyZXdlci5hbGwoKQ0KDQpgYGANCg0KDQpgYGB7ciBBcHBseWluZyBSQ29sb3JCcmV3ZXIgcGFsZXR0ZXMgdG8gQmFzZSBQbG90fQ0KcDEgKw0KICAjIGRlZmF1bHQgcGFsZXR0ZSA9ICJCbHVlcyINCiAgc2NhbGVfY29sb3VyX2JyZXdlcigpICsNCiAgbGFicyh0aXRsZSA9ICJCcmV3ZXIgUGFsZXR0ZSA9IEJsdWVzIikNCg0KcDEgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIpICsNCiAgbGFicyh0aXRsZSA9ICJCcmV3ZXIgUGFsZXR0ZSA9IFNwZWN0cmFsIikgDQoNCg0KYGBgDQoNCg0KYGBge3IgWU9VUi1UVVJOLTd9DQoNCmBgYA0KDQoNCiMjIERpc2NyZXRlIENvbG91ciBzY2FsZXMgdXNpbmcgYHdlc2FuZGVyc29uYCBwYWxldHRlcw0KDQpgYGB7ciB3ZXNhbmRlci1saXN0fQ0Kd2VzYW5kZXJzb246Ondlc19wYWxldHRlcyAlPiUgbmFtZXMoKQ0KDQpgYGANCg0KDQpgYGB7ciB3ZXNhbmRlcnNvbiBkaXNjcmV0ZX0NCnAxICsNCiAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKHR5cGUgPSB3ZXNfcGFsZXR0ZShuYW1lID0gIkdyYW5kQnVkYXBlc3QxIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IDMpKSANCiAgDQpwMSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gd2VzYW5kZXJzb246Ondlc19wYWxldHRlKG5hbWUgPSAiWmlzc291MSIsIG4gPSAzKSkNCmBgYA0KDQoNCg0KYGBge3IgWU9VUi1UVVJOLTh9DQoNCmBgYA0KDQojIyBEaXNjcmV0ZSBuLUNvbG91ciBwYWxldHRlcyBmcm9tIGBSQ29sb3JCcmV3ZXJgDQoNCmBgYHtyIEJyZXdlci1kaXNjcmV0ZX0NCiMgc2NhbGVfeF9icmV3ZXIoKSBmb3IgRElTQ1JFVEUgZGF0YQ0KcDEgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKSArDQogIA0KICBsYWJzKHRpdGxlID0gIlJDb2xvckJyZXdlciBQYWxldHRlIikNCmBgYA0KDQoNCg0KYGBge3IgWU9VUi1UVVJOLTl9DQoNCmBgYA0KDQojIyBEaXNjcmV0ZSBDb2xvdXIgc2NhbGVzIHVzaW5nIGBwYWxldHRlZXJgIHBhbGV0dGVzDQoNCmBgYHtyIHBhbGV0dGVlciBkaXNjcmV0ZX0NCnBhbGV0dGVzX2RfbmFtZXMNCnBhbGV0dGVzX2R5bmFtaWNfbmFtZXMNCnBhbGV0dGVlcl9kKCJkdXRjaG1hc3RlcnM6OnBlYXJsX2VhcnJpbmciKQ0KcGFsZXR0ZWVyX2R5bmFtaWMoImdndGhlbWVzX3B0b2w6OnF1YWxpdGF0aXZlIiwgbiA9IDMpDQoNCnAxICsNCiAgc2NhbGVfY29sb3VyX3BhbGV0dGVlcl9kKCJnZ3RoZW1lc19wdG9sOjpxdWFsaXRhdGl2ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZHluYW1pYyA9IFRSVUUpICsNCiAgDQogIGxhYnModGl0bGUgPSAiUGFsZXR0ZXMgZnJvbSBgcGFsZXR0ZWVyYCIsIA0KICAgICAgICAgIHN1YnRpdGxlID0gIkR5bmFtaWMgUGFsZXR0ZSIpDQoNCg0KIyBJIGxpa2UgVmVybWVlcidzICJHaXJsIHdpdGggdGhlIFBlYXJsIEVhcnJpbmciIQ0KcDEgKw0KICBzY2FsZV9jb2xvdXJfcGFsZXR0ZWVyX2QoImR1dGNobWFzdGVyczo6cGVhcmxfZWFycmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBkeW5hbWljID0gRkFMU0UpICsNCiAgDQogIGxhYnModGl0bGUgPSAiUGFsZXR0ZXMgZnJvbSBgcGFsZXR0ZWVyYCIsIA0KICAgICAgICAgIHN1YnRpdGxlID0gIiBQYWxldHRlIGZyb20gVmVybWVlcjogR2lybCB3aXRoIFBlYXJsIEVhcnJpbmciKQ0KICANCg0KYGBgDQoNCmBgYHtyIFlPVVItVFVSTi0xMH0NCg0KYGBgDQoNCg0K